struct f_int {
	struct usb_function	function;
	struct usb_ep		*in_ep;
};

static inline struct f_int *func_to_struct(struct usb_function *f)
{
	return container_of(f, struct f_int, function);
}

static struct usb_interface_descriptor int_intf = {
	.bLength =		sizeof int_intf,
	.bDescriptorType =	USB_DT_INTERFACE,
	.bNumEndpoints =	1,
	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
};

static struct usb_endpoint_descriptor fs_source_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,
	.bEndpointAddress =	USB_DIR_IN, /* to host */
	.bmAttributes =		USB_ENDPOINT_XFER_INT,
	.wMaxPacketSize =       __constant_cpu_to_le16(8),
	.bInterval = 	100, // determine urb packet number per second.
};

static struct usb_descriptor_header *fs_int_descs[] = {
	(struct usb_descriptor_header *) &int_intf,
	(struct usb_descriptor_header *) &fs_source_desc,
	NULL,
};

static struct usb_string strings_int[] = { [0].s = "temprobe", { } };

static struct usb_gadget_strings stringtab_int = {
	.language	= 0x0409,
	.strings	= strings_int,
};

static struct usb_gadget_strings *int_strings[] = { &stringtab_int, 0 };

static int __init
int_bind(struct usb_configuration *c, struct usb_function *f)
{
	struct usb_composite_dev *cdev = c->cdev;
	struct f_int	*fi = func_to_struct(f);
	int			id;

	id = usb_interface_id(c, f);
	if (id < 0)
		return id;
	int_intf.bInterfaceNumber = id;

	fi->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
	if (!fi->in_ep)
		return -ENODEV;
	fi->in_ep->driver_data = cdev;
	return 0;
}

static void
int_unbind(struct usb_configuration *c, struct usb_function *f)
{
	kfree(func_to_struct(f));
}

#if 0
static void int_complete(struct usb_ep *ep, struct usb_request *req)
{
	static int x=0;
	x++;
	
	if( req->status == 0 )
		printk("<3>%d complete success.\n", x);
	else
		printk("<3>complete fail.\n");
	
	if ( usb_ep_queue(ep, req, GFP_ATOMIC) )
		printk("<3>cannot queue request.\n");
}
#endif

static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
{
	if (ep->driver_data) {
		usb_ep_disable(ep);
		ep->driver_data = NULL;
	}
}

static void disable_int(struct f_int *fi)
{
	struct usb_composite_dev	*cdev;
	cdev = fi->function.config->cdev;
	disable_ep(cdev, fi->in_ep);
}

static int
enable_int(struct usb_composite_dev *cdev, struct f_int *fi)
{
	int	result = 0;
	const struct usb_endpoint_descriptor *src;
	struct usb_ep *ep;

	src = &fs_source_desc;

	ep = fi->in_ep;
	result = usb_ep_enable(ep, src);
	if (result < 0)
	  {
		printk("<3>usb_ep_enable failed. %d", result);
		return result;
	  }
	ep->driver_data = fi;

#if 0
	/* debug int endpoint code */
	struct usb_request *req;
	req = alloc_ep_req(ep);
	if(req)
	  {
		req->complete = int_complete;
		strcpy(req->buf, "abcdefgh");
		if ( usb_ep_queue(ep, req, GFP_ATOMIC) )
			printk("<3>cannot queue request.\n");
	  }
	else
		printk("<3>Cannot alloc ep req.\n");

	/* print functions */
	printk("<3>cv %d\n", (int)cdev->config->bConfigurationValue);
	printk("<3>c %d\n", (int)cdev->config->iConfiguration);
	printk("<3>f %p\n", &fi->function);
	printk("<3>0 %p\n", cdev->config->interface[0]);
	printk("<3>1 %p\n", cdev->config->interface[1]);
	printk("<3>2 %p\n", cdev->config->interface[2]);
#endif
	return result;
}

static int int_set_alt(struct usb_function *f,
		unsigned intf, unsigned alt)
{
	struct f_int	*fi = func_to_struct(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	if (fi->in_ep->driver_data)
		disable_int(fi);
	return enable_int(cdev, fi);
}

static void int_disable(struct usb_function *f)
{
	struct f_int	*fi = func_to_struct(f);

	disable_int(fi);
}

static int __init int_bind_config(struct usb_configuration *c)
{
	struct f_int	*fi;
	int			status;

	fi = kzalloc(sizeof *fi, GFP_KERNEL);
	if (!fi)
		return -ENOMEM;

	fi->function.name = "temprobe";
	fi->function.descriptors = fs_int_descs;
	fi->function.bind = int_bind;
	fi->function.unbind = int_unbind;
	fi->function.set_alt = int_set_alt;
	fi->function.disable = int_disable;

	status = usb_add_function(c, &fi->function);
	if (status)
		kfree(fi);
	return status;
}

static struct usb_configuration int_driver = {
	.label		= "temprobe",
	.strings	= int_strings,
	.bind		= int_bind_config,
	.bConfigurationValue = 1, // configuration index
	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER,
};

int __init int_add(struct usb_composite_dev *cdev)
{
	int id;

	id = usb_string_id(cdev);
	if (id < 0)
		return id;
	strings_int[0].id = id;

	int_intf.iInterface = id;
	int_driver.iConfiguration = id;

	return usb_add_config(cdev, &int_driver);
}
